#include "Math.h"

#include<cmath>
#include<numbers>

lxj::Vector3::Vector3(const double x, const double y, const double z):
    m_x(x),m_y(y),m_z(z)
{
}

lxj::Vector3::Vector3(const Vector3 &other):
    m_x(other.m_x),m_y(other.m_y),m_z(other.m_z)
{
    std::cout<<"copy\n";
}

lxj::Vector3::Vector3(Vector3 &&other):
    m_x(std::move(other.m_x)),m_y(std::move(other.m_y)),m_z(std::move(other.m_z))
{
    std::cout<<"move\n";
}

lxj::Vector3 &lxj::Vector3::operator=(const Vector3 &other)
{
    if(this==&other)    return *this;
    this->m_x=other.m_x;
    this->m_y=other.m_y;
    this->m_z=other.m_z;

    return *this;
}

const bool lxj::Vector3::operator==(const Vector3 &other)
{
    if(this==&other)    return true;

    bool is_same=(this->m_x==other.m_x && this->m_y==other.m_y && this->m_z==other.m_z) ? true:false;

    return is_same;
}

lxj::Vector3 lxj::Vector3::operator-() const
{
    return Vector3(-this->m_x, -this->m_y, -this->m_z);
}

lxj::Vector3 lxj::Vector3::operator*(const float scalar) const
{
    return Vector3(m_x*scalar, m_y*scalar, m_z*scalar);
}

lxj::Vector3 &lxj::Vector3::operator*=(const float scalar)
{
    m_x*=scalar;
    m_y*=scalar;
    m_z*=scalar;
    
    return *this;
}

lxj::Vector3 lxj::Vector3::operator/(const float scalar) const
{
    if(scalar==0){
        double maxDou=std::numeric_limits<double>::max();
        return Vector3(maxDou, maxDou, maxDou);
    }
    float reciprocal = 1/scalar;

    return (*this)*reciprocal;
}

lxj::Vector3 &lxj::Vector3::operator/=(const float scalar)
{
    if(scalar==0)   return *this;

    float reciprocal = 1/scalar;
    m_x /= reciprocal;
    m_y /= reciprocal;
    m_z /= reciprocal;

    return *this;
}

lxj::Vector3 &lxj::Vector3::operator+=(const Vector3 &lhs)
{
    this->m_x += lhs.m_x;
    this->m_y += lhs.m_y;
    this->m_z += lhs.m_z;

    return *this;
}

lxj::Vector3 &lxj::Vector3::operator-=(const Vector3 &lhs)
{
    this->m_x -= lhs.m_x;
    this->m_y -= lhs.m_y;
    this->m_z -= lhs.m_z;

    return *this;
}

void lxj::Vector3::print() const
{
    std::cout<<"x= "<<m_x<<"  "<<"y= "<<m_y<<"  "<<"z= "<<m_z<<'\n';
}

const double lxj::Vector3::getLength() const
{
    double len=std::sqrt(m_x*m_x+m_y*m_y+m_z*m_z);
    return len;
}

const lxj::Vector3 lxj::Vector3::normalized() const
{
    double len=this->getLength();
    return Vector3(m_x/len,m_y/len,m_z/len);
}

/* lxj::Vector3::~Vector3()
{
} */

lxj::Vector3 lxj::operator*(const float scalar, const Vector3 &vector)
{
    return Vector3(vector.m_x*scalar, vector.m_y*scalar, vector.m_z*scalar);
}

lxj::Vector3 lxj::operator/(const float scalar, const Vector3 &vector)
{
    if(scalar==0)   return vector;

    double reciprocal = 1/scalar;
    return Vector3(vector.m_x*reciprocal, vector.m_y*reciprocal, vector.m_z*reciprocal);
}

const lxj::Vector3 lxj::operator+(const Vector3 &rhs, const Vector3 &lhs)
{
    return Vector3(rhs.m_x+lhs.m_x, rhs.m_y+lhs.m_y, rhs.m_z+lhs.m_z);
}

const lxj::Vector3 lxj::operator-(const Vector3 &rhs, const Vector3 &lhs)
{
    return Vector3(rhs.m_x-lhs.m_x, rhs.m_y-lhs.m_y, rhs.m_z-lhs.m_z);
}

const double lxj::operator*(const Vector3 &rhs, const Vector3 &lhs)
{
    const  double dotValue=rhs.m_x*lhs.m_x + rhs.m_y*lhs.m_y + rhs.m_z*lhs.m_z;
    return dotValue;
}

const lxj::Vector3 lxj::crossProduct(const Vector3 &rhs, const Vector3 &lhs)
{
    const double x = rhs.m_y*lhs.m_z - rhs.m_z*lhs.m_y;
    const double y = rhs.m_z*lhs.m_x - rhs.m_x*lhs.m_z;
    const double z = rhs.m_x*lhs.m_y - rhs.m_y*lhs.m_x;
    return Vector3(x, y, z);
}

const double lxj::angle(const Vector3 &rhs, const Vector3 &lhs)
{
    double cos_a=(rhs*lhs)/(rhs.getLength()*lhs.getLength());

    return std::acos(cos_a)/3.14159*180;
}

const short lxj::isSameDirector(const Vector3 &rhs, const Vector3 &lhs)
{
    const double cosA=(rhs*lhs)/(rhs.getLength()*lhs.getLength());
    short i=0;
    i=cosA==0 ? 0:(cosA>0 ? 1:-1);
    return i;
}

lxj::Matrix3x3::Matrix3x3(float m11, float m12, float m13, float m21, float m22, float m23, float m31, float m32, float m33)
    :mm11(m11),mm12(m12),mm13(m13),mm21(m21),mm22(m22),mm23(m23),mm31(m31),mm32(m32),mm33(m33)
{
}

const bool lxj::Matrix3x3::operator==(const Matrix3x3 &lhs) const
{
    if(this == &lhs)    return true;
    bool b = (this->mm11==lhs.mm11) && (this->mm12==lhs.mm12) && (this->mm13==lhs.mm13) &&
             (this->mm21==lhs.mm21) && (this->mm22==lhs.mm22) && (this->mm23==lhs.mm23) &&
             (this->mm31==lhs.mm31) && (this->mm32==lhs.mm32) && (this->mm33==lhs.mm33);
    return b;
}
